﻿// Copyright 2009 William P. Hickey Jr. and William U. Walker
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//     
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//  
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using AgwpePort.Aprs;
using GMap.NET;
using GMap.NET.WindowsForms;
using GMap.NET.WindowsForms.Markers;

namespace CSReporter
{
    #region " Enumeration definitions "
    enum ViewLevel
    {
        Country = 5,
        State = 7,
        Region = 9,
        Metro = 11,
        City = 13,
        Street = 15,
        Neighborhood = 17
    }
    #endregion

    public partial class MapForm : Form
    {
        private PointLatLng pointPosition;
        private ConfigList configure;
        private Radio radio;
        private MapOverlay reports;
        private MapOverlay aprsStations;
        private MapOverlay aprsWeather;
        private MapOverlay icsFacility;
        private MapIcon selectedMarker;
        private bool isMouseDown = false;

        #region " Constructor, Load and Shown event handlers "
        public MapForm()
        {
            InitializeComponent();

            // Get the ConfigList object.
            configure = ConfigList.GetConfigList();
            // Create a new Radio object to handle packet communications.
            radio = Radio.GetRadio();

            // Configure the GMaps object.
            GMaps.Instance.UseRouteCache = true;
            GMaps.Instance.UseGeocoderCache = true;
            GMaps.Instance.UsePlacemarkCache = true;
            GMaps.Instance.Mode = AccessMode.ServerAndCache;

            // Configure the map control.             
            gMapControl.MaxZoom = 17;
            gMapControl.MinZoom = 5;
            gMapControl.Zoom = 11;
            gMapControl.CurrentPosition = new PointLatLng(configure[0].Latitude, configure[0].Longitude);
            gMapControl.MouseWheelZoomType = MouseWheelZoomType.ViewCenter;
            gMapControl.MapScaleInfoEnabled = false;
            gMapControl.DragButton = MouseButtons.Left;
            gMapControl.CenterPositionOnMouseWheel = false;
            gMapControl.EmptyTileBorders = new Pen(Color.Black);
            gMapControl.EmptytileBrush = new SolidBrush(Color.Black);
            gMapControl.EmptyTileText = "";

            // Configure and add overlays.
            aprsWeather = new MapOverlay(gMapControl, "aprsWeather");
            gMapControl.Overlays.Add(aprsWeather);
            aprsStations = new MapOverlay(gMapControl, "aprsStations");
            aprsStations.TooltipFont = new Font("Arial Black", 8);
            gMapControl.Overlays.Add(aprsStations);
            icsFacility = new MapOverlay(gMapControl, "icsFacility");
            gMapControl.Overlays.Add(icsFacility);
            reports = new MapOverlay(gMapControl, "reports");
            reports.TooltipFont = new Font("Lucida Console", 10);
            reports.TooltipFontBrush = Brushes.White;
            reports.TooltipFormat.Alignment = StringAlignment.Near;
            reports.TooltipPen.Color = Color.FromArgb(80, Color.Black);
            reports.TooltipBackground = new SolidBrush(Color.FromArgb(150, Color.Black));
            gMapControl.Overlays.Add(reports);
        }

        private void MapForm_Load(object sender, EventArgs e)
        {
        }

        private void MapForm_Shown(object sender, EventArgs e)
        {
            gMapControl.Focus();
            gMapControl.Refresh();
        }
        #endregion
        #region " Main menu item event handlers "
        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void openStreetMapToolStripMenuItem_Click(object sender, EventArgs e)
        {
            gMapControl.MapType = MapType.OpenStreetMap;
            gMapControl.ReloadMap();
            googleMapsToolStripMenuItem.Checked = false;
            googleSatelliteToolStripMenuItem.Checked = false;
            googleHybridToolStripMenuItem.Checked = false;
            googleTerrainToolStripMenuItem.Checked = false;
            virtualEarthMapToolStripMenuItem.Checked = false;
            virtualEarthSatelliteToolStripMenuItem.Checked = false;
            virtualEarthHybridToolStripMenuItem.Checked = false;
            cacheOnlyToolStripMenuItem.Enabled = true;
        }

        private void googleMapsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            gMapControl.MapType = MapType.GoogleMap;
            gMapControl.ReloadMap();
            openStreetMapToolStripMenuItem.Checked = false;
            googleSatelliteToolStripMenuItem.Checked = false;
            googleHybridToolStripMenuItem.Checked = false;
            googleTerrainToolStripMenuItem.Checked = false;
            virtualEarthMapToolStripMenuItem.Checked = false;
            virtualEarthSatelliteToolStripMenuItem.Checked = false;
            virtualEarthHybridToolStripMenuItem.Checked = false;
            cacheOnlyToolStripMenuItem.Enabled = false;
        }

        private void googleSatelliteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            gMapControl.MapType = MapType.GoogleSatellite;
            gMapControl.ReloadMap();
            openStreetMapToolStripMenuItem.Checked = false;
            googleMapsToolStripMenuItem.Checked = false;
            googleHybridToolStripMenuItem.Checked = false;
            googleTerrainToolStripMenuItem.Checked = false;
            virtualEarthMapToolStripMenuItem.Checked = false;
            virtualEarthSatelliteToolStripMenuItem.Checked = false;
            virtualEarthHybridToolStripMenuItem.Checked = false;
            cacheOnlyToolStripMenuItem.Enabled = false;
        }

        private void googleHybridToolStripMenuItem_Click(object sender, EventArgs e)
        {
            gMapControl.MapType = MapType.GoogleHybrid;
            gMapControl.ReloadMap();
            openStreetMapToolStripMenuItem.Checked = false;
            googleMapsToolStripMenuItem.Checked = false;
            googleSatelliteToolStripMenuItem.Checked = false;
            googleTerrainToolStripMenuItem.Checked = false;
            virtualEarthMapToolStripMenuItem.Checked = false;
            virtualEarthSatelliteToolStripMenuItem.Checked = false;
            virtualEarthHybridToolStripMenuItem.Checked = false;
            cacheOnlyToolStripMenuItem.Enabled = false;
        }

        private void googleTerrainToolStripMenuItem_Click(object sender, EventArgs e)
        {
            gMapControl.MapType = MapType.GoogleTerrain;
            gMapControl.ReloadMap();
            openStreetMapToolStripMenuItem.Checked = false;
            googleMapsToolStripMenuItem.Checked = false;
            googleSatelliteToolStripMenuItem.Checked = false;
            googleHybridToolStripMenuItem.Checked = false;
            virtualEarthMapToolStripMenuItem.Checked = false;
            virtualEarthSatelliteToolStripMenuItem.Checked = false;
            virtualEarthHybridToolStripMenuItem.Checked = false;
            cacheOnlyToolStripMenuItem.Enabled = false;
        }

        private void virtualEarthMapToolStripMenuItem_Click(object sender, EventArgs e)
        {
            gMapControl.MapType = MapType.VirtualEarthMap;
            gMapControl.ReloadMap();
            openStreetMapToolStripMenuItem.Checked = false;
            googleMapsToolStripMenuItem.Checked = false;
            googleSatelliteToolStripMenuItem.Checked = false;
            googleHybridToolStripMenuItem.Checked = false;
            googleTerrainToolStripMenuItem.Checked = false;
            virtualEarthSatelliteToolStripMenuItem.Checked = false;
            virtualEarthHybridToolStripMenuItem.Checked = false;
            cacheOnlyToolStripMenuItem.Enabled = false;
        }

        private void virtualEarthHybridToolStripMenuItem_Click(object sender, EventArgs e)
        {
            gMapControl.MapType = MapType.VirtualEarthHybrid;
            gMapControl.ReloadMap();
            openStreetMapToolStripMenuItem.Checked = false;
            googleMapsToolStripMenuItem.Checked = false;
            googleSatelliteToolStripMenuItem.Checked = false;
            googleHybridToolStripMenuItem.Checked = false;
            googleTerrainToolStripMenuItem.Checked = false;
            virtualEarthMapToolStripMenuItem.Checked = false;
            virtualEarthSatelliteToolStripMenuItem.Checked = false;
            cacheOnlyToolStripMenuItem.Enabled = false;
        }

        private void virtualEarthSatelliteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            gMapControl.MapType = MapType.VirtualEarthSatellite;
            gMapControl.ReloadMap();
            openStreetMapToolStripMenuItem.Checked = false;
            googleMapsToolStripMenuItem.Checked = false;
            googleSatelliteToolStripMenuItem.Checked = false;
            googleHybridToolStripMenuItem.Checked = false;
            googleTerrainToolStripMenuItem.Checked = false;
            virtualEarthMapToolStripMenuItem.Checked = false;
            virtualEarthHybridToolStripMenuItem.Checked = false;
            cacheOnlyToolStripMenuItem.Enabled = false;
        }

        private void serverOnlyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            serverAndCacheToolStripMenuItem.Checked = false;
            cacheOnlyToolStripMenuItem.Checked = false;
        }

        private void serverAndCacheToolStripMenuItem_Click(object sender, EventArgs e)
        {
            serverOnlyToolStripMenuItem.Checked = false;
            cacheOnlyToolStripMenuItem.Checked = false;
        }

        private void cacheOnlyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            serverOnlyToolStripMenuItem.Checked = false;
            serverAndCacheToolStripMenuItem.Checked = false;
        }
        #endregion
        #region " Toolbar button event handlers "
        private void maxExtentsToolStripButton_Click(object sender, EventArgs e)
        {
            gMapControl.ZoomAndCenterMarkers("reports");
        }

        private void zoomInToolStripButton_Click(object sender, EventArgs e)
        {
            gMapControl.CurrentPosition = CurrentViewCenter();
            if (gMapControl.Zoom < gMapControl.MaxZoom)
                gMapControl.Zoom++;
        }

        private void zoomOutToolStripButton_Click(object sender, EventArgs e)
        {
            gMapControl.CurrentPosition = CurrentViewCenter();
            if (gMapControl.Zoom > gMapControl.MinZoom)
                gMapControl.Zoom--;
        }

        private void zoomHomeToolStripButton_Click(object sender, EventArgs e)
        {
            gMapControl.Zoom = 15;
            gMapControl.CurrentPosition = new PointLatLng(configure[0].Latitude, configure[0].Longitude);
        }
        #endregion
        #region " Zoom button event handlers "
        private void zoomInMapButton_Click(object sender, EventArgs e)
        {
            zoomInToolStripButton_Click(sender, e);
        }

        private void zoomOutMapButton_Click(object sender, EventArgs e)
        {
            zoomOutToolStripButton_Click(sender, e);
        }
        #endregion
        #region " Map / Marker click event handlers "
        private void gMapControl_OnMarkerClick(GMapMarker item)
        {
            //MessageBox.Show("Marker Clicked.");
        }

        private void gMapControl_OnMarkerEnter(GMapMarker item)
        {
            selectedMarker = item as MapIcon;
            if (selectedMarker.CSReport != null)
                markerContextMenuStrip.Items[0].Visible = true;
            else
                markerContextMenuStrip.Items[0].Visible = false;
        }

        private void gMapControl_OnMarkerLeave(GMapMarker item)
        {
            selectedMarker = null;
        }

        void gMapControl_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left & gMapControl.IsMouseOverMarker)
            {
                isMouseDown = true;
                pointPosition = selectedMarker.Position;
            }
        }

        void gMapControl_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                isMouseDown = false;
                if (selectedMarker != null)
                {
                    if (selectedMarker.Position != pointPosition)
                    {
                        if (selectedMarker.CSReport != null)
                        {
                            if (MessageBox.Show("You have modified a Status Report location.\nDo you wish to save and transmit the updated information?", "Confirm Move", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                            {
                                selectedMarker.CSReport.Latitude = new Coordinate(gMapControl.FromLocalToLatLng(e.X, e.Y).Lat).ToAprsLatString();
                                selectedMarker.CSReport.Longitude = new Coordinate(gMapControl.FromLocalToLatLng(e.X, e.Y).Lng).ToAprsLonString();
                                ReportForm rf = new ReportForm();
                                rf.EditReport(selectedMarker.CSReport, false);
                                rf.Show();
                                //radio.SendCSReport(selectedMarker.CSReport);
                            }
                        }
                    }
                }
            }
        }

        private void gMapControl_MouseMove(object sender, MouseEventArgs e)
        {
            latitudeToolStripStatusLabel.Text = "Latitude: " + gMapControl.FromLocalToLatLng(e.X, e.Y).Lat.ToString();
            longitudeToolStripStatusLabel.Text = "Longitude: " + gMapControl.FromLocalToLatLng(e.X, e.Y).Lng.ToString();

            if (e.Button == MouseButtons.Left & isMouseDown & selectedMarker != null)
            {
                if(!selectedMarker.Station.Equals("APRS") & selectedMarker.CallSign.Equals(configure[0].CallSign))
                    selectedMarker.Position = gMapControl.FromLocalToLatLng(e.X, e.Y);
            }
        }

        private void gMapControl_MouseClick(object sender, MouseEventArgs e)
        {
            if (!gMapControl.IsDragging & e.Button == MouseButtons.Right)
            {
                pointPosition = gMapControl.FromLocalToLatLng(e.X, e.Y);
                if (radio.IsOpen)
                {
                    sendMessageToolStripMenuItem.Enabled = true;
                }
                else
                {
                    sendMessageToolStripMenuItem.Enabled = false;
                }
                if (gMapControl.IsMouseOverMarker)
                {
                    markerContextMenuStrip.Show(gMapControl, e.Location);
                }
                else
                {
                    mapContextMenuStrip.Show(gMapControl, e.Location);
                }
            }
        }

        private void gMapControl_OnMapZoomChanged()
        {
            zoomLevelToolStripStatusLabel.Text = " Zoom Level: " + gMapControl.Zoom.ToString();
        }

        private void gMapControl_OnTileLoadStart(int loaderId)
        {
            this.UseWaitCursor = true;
        }

        private void gMapControl_OnTileLoadComplete(int loaderId)
        {
            this.UseWaitCursor = false;
        }

        private void gMapControl_Resize(object sender, EventArgs e)
        {
        }

        private void gMapControl_OnMapDrag()
        {
        }
        #endregion
        #region " Context menu item event handlers "
        private void createStatusReportToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ReportForm rf = new ReportForm();
            rf.NewReport(pointPosition.Lat, pointPosition.Lng, configure[0].CallSign);
            rf.Show();
        }

        private void baseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MapIcon icon = new MapIcon(configure[0].CallSign, "", pointPosition, icsImageList.Images[0]);
            icsFacility.Markers.Add(icon);
        }

        private void campToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MapIcon icon = new MapIcon(configure[0].CallSign, "", pointPosition, icsImageList.Images[1]);
            icsFacility.Markers.Add(icon);
        }

        private void commandPostToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MapIcon icon = new MapIcon(configure[0].CallSign, "", pointPosition, icsImageList.Images[2]);
            icsFacility.Markers.Add(icon);
        }

        private void fireStationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MapIcon icon = new MapIcon(configure[0].CallSign, "", pointPosition, icsImageList.Images[3]);
            icsFacility.Markers.Add(icon);
        }

        private void firstAidStationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MapIcon icon = new MapIcon(configure[0].CallSign, "", pointPosition, icsImageList.Images[4]);
            icsFacility.Markers.Add(icon);
        }

        private void heliBaseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MapIcon icon = new MapIcon(configure[0].CallSign, "", pointPosition, icsImageList.Images[6]);
            icsFacility.Markers.Add(icon);
        }

        private void heliSpotToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MapIcon icon = new MapIcon(configure[0].CallSign, "", pointPosition, icsImageList.Images[7]);
            icsFacility.Markers.Add(icon);
        }

        private void iRDownlinkToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MapIcon icon = new MapIcon(configure[0].CallSign, "", pointPosition, icsImageList.Images[8]);
            icsFacility.Markers.Add(icon);
        }

        private void mobileWeatherStationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MapIcon icon = new MapIcon(configure[0].CallSign, "", pointPosition, icsImageList.Images[9]);
            icsFacility.Markers.Add(icon);
        }

        private void repeaterToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MapIcon icon = new MapIcon(configure[0].CallSign, "", pointPosition, icsImageList.Images[10]);
            icsFacility.Markers.Add(icon);
        }

        private void stagingAreaToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MapIcon icon = new MapIcon(configure[0].CallSign, "", pointPosition, icsImageList.Images[11]);
            icsFacility.Markers.Add(icon);
        }

        private void telephoneToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MapIcon icon = new MapIcon(configure[0].CallSign, "", pointPosition, icsImageList.Images[12]);
            icsFacility.Markers.Add(icon);
        }

        private void waterSourceToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MapIcon icon = new MapIcon(configure[0].CallSign, "", pointPosition, icsImageList.Images[13]);
            icsFacility.Markers.Add(icon);
        }

        private void setLocationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("You have requested to change your default station coordinates to this location.\nDo you wish to continue?", "Confirm Change", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                configure[0].Latitude = pointPosition.Lat;
                configure[0].Longitude = pointPosition.Lng;
                configure.WriteXml();
            }
        }

        private void editStatusReportToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (selectedMarker != null)
            {
                ReportForm rf = new ReportForm();
                rf.EditReport(selectedMarker.CSReport, false);
                rf.Show();
                selectedMarker = null;
            }
        }

        private void sendMessageToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (radio.IsOpen & selectedMarker != null)
            {
                MessageForm mf = new MessageForm(selectedMarker.CallSign);
                mf.ShowDialog();
                selectedMarker = null;
            }
        }

        private void addToStationsDBToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (selectedMarker != null)
            {
                if (selectedMarker.Station.Length > 0)
                {
                    Station station = new Station();
                    station.CallSign = selectedMarker.Station;
                    station.Latitude = selectedMarker.Position.Lat;
                    station.Longitude = selectedMarker.Position.Lng;
                    StationList.GetStationList().AddStation(station);
                    StationList.GetStationList().WriteXml(configure[0].DataDir + @"\stations.xml");
                }
                selectedMarker = null;
            }
        }

        private void markerContextMenuStrip_MouseLeave(object sender, EventArgs e)
        {
            markerContextMenuStrip.Close(ToolStripDropDownCloseReason.CloseCalled);
        }
        #endregion
        #region " Additional methods and handlers "
        public PointLatLng CurrentViewCenter()
        {
            double left = gMapControl.CurrentViewArea.Left;
            double right = gMapControl.CurrentViewArea.Right;
            double top = gMapControl.CurrentViewArea.Top;
            double bottom = gMapControl.CurrentViewArea.Bottom;
            return new PointLatLng(top - ((top - bottom) / 2), left - ((left - right) / 2));
        }

        public void MapStation(AprsData pkt, string source, Image image)
        {
            PointLatLng position = new PointLatLng(pkt.Latitude, pkt.Longitude);
            if (pkt.Speed > 0 & !pkt.SymbolTableCode.EndsWith("_"))
                image = RotateImage(image, pkt.Course + 90);
            MapIcon icon = new MapIcon(source, "APRS", position, image);

            icon.TooltipMode = MarkerTooltipMode.OnMouseOver;
            icon.ToolTipText = " " + source;
            aprsStations.Markers.Add(icon);
        }

        public void MapFacility(AprsData pkt, string source, Image image)
        {
            PointLatLng position = new PointLatLng(pkt.Latitude, pkt.Longitude);
            MapIcon icon = new MapIcon(source, source, position, image);

            icon.TooltipMode = MarkerTooltipMode.OnMouseOver;
            icon.ToolTipText = pkt.Comment;
            aprsStations.Markers.Add(icon);
        }

        public void MapCSReport(double latitude, double longitude, CSReportMessage csReport)
        {
            PointLatLng position = new PointLatLng(latitude, longitude);
            MapIcon icon = new MapIcon(csReport, position, icsImageList.Images[14]);

            icon.TooltipMode = MarkerTooltipMode.OnMouseOver;
            icon.ToolTipOffset.X = 40;
            icon.ToolTipOffset.Y = -75;
            icon.ToolTipText = "  Reported by: " + csReport.Station +
                             "\n      Contact: " + csReport.FromCall +
                             "\n    Report ID: " + csReport.ReportID.ToString().PadLeft(4,'0') +
                             "\nIncident Time: " + AprsCalc.TimeStampToDateTime(csReport.IncidentTime).ToString() +
                             "\n     Comments: " + csReport.DetailText;
            reports.Markers.Add(icon);
        }

        public void ClearCSReports()
        {
            reports.Markers.Clear();
        }

        public void ClearAprsStations()
        {
            aprsStations.Markers.Clear();
        }

        /// <summary>
        /// Method to rotate an image either clockwise or counter-clockwise
        /// Positive values will rotate clockwise and negative values will rotate counter-clockwise.
        /// </summary>
        /// <param name="img">Image to be rotated</param>
        /// <param name="rotationAngle">Rotation angle (in degrees)</param>
        /// <returns></returns>
        public static Image RotateImage(Image img, float rotationAngle)
        {
            //create an empty Bitmap image
            Bitmap bmp = new Bitmap(img.Width, img.Height);

            //turn the Bitmap into a Graphics object
            Graphics gfx = Graphics.FromImage(bmp);

            //now we set the rotation point to the center of our image
            gfx.TranslateTransform((float)bmp.Width / 2, (float)bmp.Height / 2);

            //now rotate the image
            gfx.RotateTransform(rotationAngle);

            gfx.TranslateTransform(-(float)bmp.Width / 2, -(float)bmp.Height / 2);

            //set the InterpolationMode to HighQualityBicubic so to ensure a high
            //quality image once it is transformed to the specified size
            gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;

            //now draw our new image onto the graphics object
            gfx.DrawImage(img, new System.Drawing.Point(0, 0));

            //dispose of our Graphics object
            gfx.Dispose();

            //return the image
            return bmp;
        }
        #endregion
    }
}
